#!/usr/bin/env python
# encoding:utf8

import os
import sys
import pdb
import traceback

from multiprocessing import Process
from threading import Thread
from datetime import datetime, date

from scheduler.parameter_parser import parse_parameter
from scheduler.function_parser import parse_function
from scheduler.get_trigger import get_trigger
from triggers.util import convert_to_datetime
from utils.dec_ignore_exception import ignore_exception


class TaskerWrapper(object):
    def __init__(self, tasker):
        self.tasker = tasker
        self.id = tasker.id
        self.title = tasker.title
        self.app_name = tasker.func
        self.args = tasker.args

        self.cron_type = tasker.cron_type
        self.cron_params = tasker.cron_params
        self.process = None  # main processer

    def get_trigger(self):
        return get_trigger(self.cron_type, self.cron_params)

    def run_instance(self):
        self.process = Process(name=self.app_name, target=self.func_wrapper, args=(self.app_name, self.args))
        self.process.daemon = True
        self.process.start()

    def func_wrapper(self, app_name, args):
        try:
            func = parse_function(app_name)
            args = parse_parameter(args)
        except Exception, ex:
            info = traceback.format_exc()
            self.update_tasker(pid="", status=-1, info=info)
            return

        try:
            # 解析函数
            self.update_tasker(pid=os.getpid(), start_time=datetime.now(), end_time=None, status=10, info="")
            data = func(**args)
            self.update_tasker(pid="", end_time=datetime.now(), info="Done", status=0)
        except Exception, ex:
            info = traceback.format_exc()
            self.update_tasker(pid="", end_time=None, status=-1, info=info)

    def update_tasker(self, **kwargs):
        update_fields = []
        for k, v in kwargs.iteritems():
            setattr(self.tasker, k, v)
            update_fields.append(k)
        if update_fields:
            self.tasker.save(update_fields=update_fields)

    @property
    def last_fire_time(self):
        return self.tasker.start_time

    @last_fire_time.setter
    def last_fire_time(self, value):
        self.tasker.start_time = value
        self.tasker.save(update_fields=['start_time'])

    @property
    def status(self):
        return self.tasker.status

    @status.setter
    def status(self, value):
        self.tasker.status = value
        self.tasker.save(update_fields=['status'])

    @property
    def is_running(self):
        if self.process and self.process.is_alive():
            return True
        return False

    @ignore_exception
    @property
    def pid(self):
        if self.is_running:
            return self.process.pid
        return None

    def clear(self):
        if not self.is_running:
            self.process = None

    @ignore_exception
    def shutdown(self):
        if self.is_running:
            self.process.terminate()
            self.update_tasker(pid="", end_time=datetime.now(), status=-1, info="shutdown")
        self.clear()

    def info(self):
        return "id:[%s] name:[%s] type:[%s] params:[%s]"%(
            self.id, self.title, self.cron_type, self.cron_params)

    def get_next_fire_time(self, last_fire_time, now):
        # 这个地方的task是父类的tasker
        try:
            trigger = self.get_trigger()
        except Exception, ex:
            info = traceback.format_exc()
            n = datetime.now()
            self.update_tasker(pid="", start_time=n, end_time=n, status=-1, info=info)
            return
        # should be of type with timezone
        last_fire_time = convert_to_datetime(last_fire_time)
        now = convert_to_datetime(now)
        return trigger.get_next_fire_time(last_fire_time, now)


# deprecated
class TimerCronJobThread(object):
    ''' job timer for func starting
    :param func:
    :param args:
    :param kargs:
    :param timer_options: (hour, minute, second)
    :return:
    '''
    def __init__(self, name, func, args=(), kwargs={}, hour=8, minute=0, second=0):
        self.name = name
        self.func = func
        self.args = args
        self.kwargs = kwargs

        self.hour = self.check_time(hour, 0, 24, 0)
        self.minute = self.check_time(minute, 0, 60, 0)
        self.second = self.check_time(second, 0, 60, 0)

        self.process = None
        self.last_run_date = None
        self.today_finished = False

    def run(self):
        today = date.today()
        if today != self.last_run_date:
            self.run_instance()

    def run_instance(self):
        if not self.is_running:
            self.process = Thread(name=self.name, target=self.func, args=self.args, kwargs=self.kwargs)
            self.process.setDaemon(True)
            self.last_run_date = date.today()
            self.process.start()

    @property
    def is_running(self):
        print >>sys.stdout, ">>>>", self.process, self.process.isAlive()
        if self.process and self.process.isAlive():
            return True
        return False

    @ignore_exception
    @property
    def pid(self):
        if self.is_running:
            return self.process.pid
        return None

    def get_start_time(self):
        return "%s%s%s"%(self.hour, self.minute, self.second)

    def check_time(self, value, min, max, default):
        # to "08"/'20'
        if not value: value = 0
        try:
            value = int(value)
        except:
            value = default
        if value > max: value = max
        if value < min: value = min
        return "{0:0>2}".format(value)

    def clear(self):
        if not self.is_running:
            self.process = None

    @ignore_exception
    def shutdown(self):
        # TODO
        raise NotImplementedError("thread could not be shutdown")

    def info(self):
        return "name:[%s] st:[%s]"%(self.name, self.get_start_time())